package com.sven.sharedb.core.db;

import com.google.common.collect.Range;
import com.sven.sharedb.core.entity.ShardingTableDateRange;
import com.sven.sharedb.core.exception.BaseException;
import com.sven.sharedb.core.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 创建日期按量分片规则
 * <P/>标准分片算法
 * <P/>用于处理使用单一键作为分片键的 =、IN、BETWEEN AND、>、<、>=、<= 进行分片的场景。
 * @author sven.wang
 * @version 2022/6/30 16:17
 */
@Slf4j
@Component
public class CountCreateDateShardingTableAlgorithm implements StandardShardingAlgorithm<Date> {

    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Date> preciseShardingValue) {
        if(log.isDebugEnabled()){
            log.debug("doSharding Precise");
        }
        //查询时间
        Date queryDateTime = preciseShardingValue.getValue();
        if(log.isDebugEnabled()){
            log.debug("精准分片查询条件的值：{}",DateTimeUtils.date2StrDateTime(queryDateTime));
        }
        List<ShardingTableDateRange> tables = ShardingContUtils.getTableInfo(new ArrayList<>(collection));
        Optional<ShardingTableDateRange> first = tables.stream().filter(item -> ShardingContUtils.isShardingTableDatePrecise(item, queryDateTime)).findFirst();
        if(!first.isPresent()){
            log.error("查询的时间不在分表记录内！全部表名:{}，查询时间:{}", GsonUtils.toJson(tables), DateTimeUtils.date2StrDateTime(queryDateTime));
            throw new BaseException("查询的时间不在分表记录内！");
        }
        return first.get().getTableName();
    }

    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Date> rangeShardingValue) {
        if(log.isDebugEnabled()){
            log.debug("doSharding Range");
        }
        //查询时间
        Range<Date> queryDateTimes = rangeShardingValue.getValueRange();
        //范围分片的最小值
        Date lowerDate = null;
        //范围分片的最大值
        Date upperDate = null;
        try{
            lowerDate = queryDateTimes.lowerEndpoint();
        } catch (IllegalStateException e){
            log.info("本次查询没有最小值");
        }
        try{
            upperDate = queryDateTimes.upperEndpoint();
        } catch (IllegalStateException e){
            log.info("本次查询没有最大值");
        }
        if(log.isDebugEnabled()){
            log.debug("范围分片查询条件的最小值：{}，最大值：{}",
                    lowerDate != null ? DateTimeUtils.date2StrDateTime(lowerDate) : "",
                    upperDate != null ? DateTimeUtils.date2StrDateTime(upperDate) : "");
        }
        List<ShardingTableDateRange> tables = ShardingContUtils.getTableInfo(new ArrayList<>(collection));
        Date finalLowerDate = lowerDate;
        Date finalUpperDate = upperDate;
        List<ShardingTableDateRange> collect = tables.stream()
                .filter(item -> ShardingContUtils.isShardingTableDateRange(item, finalLowerDate, finalUpperDate)).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(collect)){
            log.error("查询的时间不在分表记录内！全部表名:{}，查询时间下界:{}，查询时间上界:{}", GsonUtils.toJson(tables), DateTimeUtils.date2StrDateTime(lowerDate), DateTimeUtils.date2StrDateTime(upperDate));
            throw new BaseException("查询的时间不在分表记录内！");
        }
        return collect.stream().map(ShardingTableDateRange::getTableName).collect(Collectors.toList());
    }

    @Override
    public void init() {
        if(log.isDebugEnabled()){
            log.debug("doSharding CountCreateDate init");
        }
    }

    @Override
    public String getType() {
        if(log.isDebugEnabled()){
            log.debug("doSharding CountCreateDate getType");
        }
        return "CountCreateDateShardingTableAlgorithm";
    }
}
